Understand how Python can do many things at once — and why that matters for API calls.
Day 61 of 80
Right now, when you generate prompts for 3 platforms, your code does this:
With async, it does this:
Same work, 3x faster. For batch generation (20 shots × 3 platforms = 60 API calls), the difference is minutes vs. seconds.
Synchronous waiter: Takes table 1's order → walks to kitchen → waits for food → brings it back → walks to table 2. Takes table 2's order → walks to kitchen → waits for food → brings it back. Customers at table 2 wait for the whole cycle.
Async waiter: Takes table 1's order → sends it to kitchen → takes table 2's order → sends it → takes table 3's order → sends it → brings food to tables as it comes out. Kitchen is doing the slow work. The waiter (your Python code) never just stands around waiting.
One waiter. Same kitchens. But the waiter never blocks — it moves to the next task whenever it's waiting on something else. That's async.
Python Asyncio Tutorial — Corey Schafer. The definitive Python asyncio tutorial. Type along with every example.
What to pay attention to:
async def and regular defawait does — it says "pause here until this is done, but let other tasks run while we wait"asyncio.gather() — the key pattern. Fire multiple coroutines and collect all results.asyncio.run() — how you start an async program from regular Python codeReal Python — Async IO in Python — skim the first half for conceptual understanding. The analogy in this article (chess grandmaster vs. amateur players) is one of the clearest explanations of async I've seen.
async def defines a coroutine — a function that can pause and resume. await is the pause point. When Python hits await, it pauses that coroutine and runs other ready coroutines until the awaited thing completes.
# Regular function — can't pause, blocks everything
def regular():
result = slow_operation() # blocks for 2 seconds
return result
# Async function — can pause at 'await', lets other things run
async def async_version():
result = await slow_operation() # pauses here, but doesn't block other tasks
return result
import asyncio
async def main():
# Run three things in parallel. gather() starts them all,
# then waits for all to finish and returns all results.
results = await asyncio.gather(
task_one(),
task_two(),
task_three(),
)
# results is a list: [result_of_one, result_of_two, result_of_three]
# They're in the same order as you passed them in.
return results
asyncio.run(main()) # starts the async event loop
The Anthropic SDK has an async version of the client. Same API, but every method call needs await:
# Sync (one at a time):
client = anthropic.Anthropic()
message = client.messages.create(...) # blocks
# Async (can run in parallel):
client = anthropic.AsyncAnthropic()
message = await client.messages.create(...) # pauses without blocking
Async doesn't make CPU-bound work faster. If you're doing heavy computation (image processing, math), asyncio won't help — you'd need threads or multiprocessing for that.
Async only helps with I/O-bound work — things where Python is waiting on something external: network requests, file reads, database queries. API calls are the perfect use case.
async def and await doasyncio.gather() doesasyncio.run()Day 62 experiments with async code in Jupyter — including your first parallel Claude API calls. You'll see the speed difference directly.